package bleudp

import (
	"context"
	"encoding/hex"
	"errors"
	"iot-ble-server/global/globalconstants"
	"iot-ble-server/global/globalredis"
	"iot-ble-server/global/globalstruct"
	"iot-ble-server/global/globaltransfersn"
	"iot-ble-server/global/globalutils"
	"iot-ble-server/internal/config"
	"iot-ble-server/internal/packets"
	"strings"
)

//下行数据统一生成
func GenerateDownJsonInfo(ctx context.Context, unifyMsg *globalstruct.DownJsonInfoUnify) (jsonInfo packets.JsonUdpInfo, err error) {
	if (unifyMsg.Ctrl & 1) == 1 {
		jsonInfo.MessageHeader, err = GenerateMessageHeader(ctx, unifyMsg)
		if err != nil {
			return
		}
	}
	if ((unifyMsg.Ctrl >> 1) & 1) == 1 {
		jsonInfo.MessageBody, err = GenerateMessageBody(unifyMsg)
		if err != nil {
			return
		}
	}
	if ((unifyMsg.Ctrl >> 2) & 1) == 1 {
		jsonInfo.MessageAppHeader, err = GenerateMessageAppHeader(ctx, unifyMsg)
		if err != nil {
			return
		}
	}
	if ((unifyMsg.Ctrl >> 3) & 1) == 1 {
		jsonInfo.MessageAppBody, err = GenerateMessageAppBody(unifyMsg)
		if err != nil {
			return
		}
	}
	jsonInfo.PendCtrl = unifyMsg.Ctrl
	return
}

func GenerateMessageHeader(ctx context.Context, unifyInfo *globalstruct.DownJsonInfoUnify) (resultJson packets.MessageHeader, err error) {
	transferLinkedKey := globalutils.CreateCacheKey(globalconstants.GwRedisSNTransfer, unifyInfo.GwMac)
	var curFrameNum int64
	resultJson.Version = packets.Version3
	resultJson.LinkMsgType = unifyInfo.LinkedMsgType
	switch unifyInfo.LinkedMsgType {
	case packets.GatewayDevInfo, packets.IotModuleRset, packets.IotModuleStatusChange:
		resultJson.OpType = packets.RequireWithResp
	case packets.IotAccessMsg:
		resultJson.OpType = packets.Request
	default:
		return resultJson, errors.New("unrecognized LinkMessage")
	}
	if config.C.General.UseRedis {
		curFrameNum, err = globalredis.RedisCache.Incr(ctx, transferLinkedKey).Result()
		if err != nil {
			return packets.MessageHeader{}, err
		}
		if curFrameNum > int64(globalconstants.MaxMessageFourLimit) {
			globalredis.RedisCache.Del(ctx, transferLinkedKey)
			curFrameNum, err = globalredis.RedisCache.Incr(ctx, transferLinkedKey).Result()
			if err != nil {
				return packets.MessageHeader{}, err
			}
		}
	} else {
		globaltransfersn.TransferSN.Lock()
		if globaltransfersn.TransferSN.SN[transferLinkedKey] < 1 {
			globaltransfersn.TransferSN.SN[transferLinkedKey] = 1
		} else {
			globaltransfersn.TransferSN.SN[transferLinkedKey] = globaltransfersn.TransferSN.SN[transferLinkedKey] + 1
			if globaltransfersn.TransferSN.SN[transferLinkedKey] > globalconstants.MaxMessageFourLimit {
				globaltransfersn.TransferSN.SN[transferLinkedKey] = 1
			}
		}
		curFrameNum = int64(globaltransfersn.TransferSN.SN[transferLinkedKey])
		globaltransfersn.TransferSN.Unlock()
	}
	resultJson.LinkMsgFrameSN = globalutils.ConvertDecimalToHexStr(int(curFrameNum), globalconstants.BYTE_STR_EIGHT)
	return
}

func GenerateMessageBody(unifyInfo *globalstruct.DownJsonInfoUnify) (resultJson packets.MessageBody, err error) {
	switch unifyInfo.LinkedMsgType {
	case packets.IotAccessMsg, packets.GatewayDevInfo, packets.IotModuleRset, packets.IotModuleStatusChange:
		resultJson.GwMac = unifyInfo.GwMac
		resultJson.ModuleID = unifyInfo.IotModuleId
	default:
		return resultJson, errors.New("unrecognized LinkMessage")
	}
	return
}

func GenerateMessageAppHeader(ctx context.Context, unifyInfo *globalstruct.DownJsonInfoUnify) (resultJson packets.MessageAppHeader, err error) {
	var (
		curFrameNum    int64
		transferDevKey = globalconstants.BleRedisSNTransfer + unifyInfo.GwMac + unifyInfo.IotModuleId
	)
	resultJson.CtrlField = globalconstants.CtrlField
	resultJson.FragOffset = globalconstants.FragOffset
	switch unifyInfo.ApplicationMsgType {
	case packets.BleRequest:
		resultJson.OpType = packets.RequireWithResp
	default:
		return resultJson, errors.New("unrecognized BLEMessage")
	}
	if config.C.General.UseRedis {
		curFrameNum, err = globalredis.RedisCache.Incr(ctx, transferDevKey).Result()
		if err != nil {
			return
		}
		if curFrameNum > int64(globalconstants.MaxMessageTwoLimit) {
			globalredis.RedisCache.Del(ctx, transferDevKey)
			curFrameNum, err = globalredis.RedisCache.Incr(ctx, transferDevKey).Result()
			if err != nil {
				return
			}
		}
	} else {
		globaltransfersn.TransferSN.Lock()
		if globaltransfersn.TransferSN.SN[transferDevKey] < 1 {
			globaltransfersn.TransferSN.SN[transferDevKey] = 1
		} else {
			globaltransfersn.TransferSN.SN[transferDevKey] = globaltransfersn.TransferSN.SN[transferDevKey] + 1
			if globaltransfersn.TransferSN.SN[transferDevKey] > globalconstants.MaxMessageTwoLimit {
				globaltransfersn.TransferSN.SN[transferDevKey] = 1
			}
		}
		curFrameNum = int64(globaltransfersn.TransferSN.SN[transferDevKey])
		globaltransfersn.TransferSN.Unlock()
	}
	resultJson.SN = globalutils.ConvertDecimalToHexStr(int(curFrameNum), globalconstants.BYTE_STR_FOUR)
	resultJson.Type = unifyInfo.ApplicationMsgType
	return
}

//应用消息生成
func GenerateMessageAppBody(unifyInfo *globalstruct.DownJsonInfoUnify) (resultJson packets.MessageAppBody, err error) {
	switch unifyInfo.TlvMsgType {
	case packets.TLVConnectMsg:
		resultJson.TLV.TLVMsgType = packets.TLVConnectMsg
		resultJson.TLV.TLVPayload.ReserveOne = globalutils.ConvertDecimalToHexStr(0, globalconstants.BYTE_STR_TWO)
		resultJson.TLV.TLVPayload.AddrType = globalutils.ConvertDecimalToHexStr(int(unifyInfo.DevConnInfo.AddrType), globalconstants.BYTE_STR_TWO)
		resultJson.TLV.TLVPayload.DevMac = unifyInfo.DevConnInfo.DevMac
		resultJson.TLV.TLVPayload.ReserveTwo = globalutils.ConvertDecimalToHexStr(0, globalconstants.BYTE_STR_TWO)
		resultJson.TLV.TLVPayload.ScanType = globalutils.ConvertDecimalToHexStr(int(unifyInfo.DevConnInfo.ScanType), globalconstants.BYTE_STR_TWO)
		resultJson.TLV.TLVPayload.ReserveThree = globalutils.ConvertDecimalToHexStr(0, globalconstants.BYTE_STR_TWO)
		resultJson.TLV.TLVPayload.ScanInterval = globalutils.ConvertDecimalToHexStr(int(unifyInfo.DevConnInfo.ScanInterval), globalconstants.BYTE_STR_FOUR)
		resultJson.TLV.TLVPayload.ScanWindow = globalutils.ConvertDecimalToHexStr(int(unifyInfo.DevConnInfo.ScanWindow), globalconstants.BYTE_STR_FOUR)
		resultJson.TLV.TLVPayload.ScanTimeout = globalutils.ConvertDecimalToHexStr(int(unifyInfo.DevConnInfo.ScanTimeout), globalconstants.BYTE_STR_FOUR)
		resultJson.TLV.TLVPayload.ConnInterval = globalutils.ConvertDecimalToHexStr(int(unifyInfo.DevConnInfo.ConnInterval), globalconstants.BYTE_STR_FOUR)
		resultJson.TLV.TLVPayload.ConnLatency = globalutils.ConvertDecimalToHexStr(int(unifyInfo.DevConnInfo.ConnLatency), globalconstants.BYTE_STR_FOUR)
		resultJson.TLV.TLVPayload.SupervisionWindow = globalutils.ConvertDecimalToHexStr(int(unifyInfo.DevConnInfo.SupervisionWindow), globalconstants.BYTE_STR_FOUR)
	case packets.TLVMainServiceReqMsg:
		resultJson.TLV.TLVMsgType = packets.TLVMainServiceReqMsg
		resultJson.TLV.TLVPayload.DevMac = unifyInfo.TerminalMac
	case packets.TLVMainServiceByUUIDReqMsg:
		resultJson.TLV.TLVMsgType = packets.TLVMainServiceByUUIDReqMsg
		resultJson.TLV.TLVPayload.DevMac = unifyInfo.TerminalMac
		resultJson.TLV.TLVPayload.ServiceUUID = unifyInfo.UUID
	case packets.TLVCharFindReqMsg:
		resultJson.TLV.TLVMsgType = packets.TLVCharFindReqMsg
		resultJson.TLV.TLVPayload.DevMac = unifyInfo.CharacterFind.DevMac
		resultJson.TLV.TLVPayload.StartHandle = unifyInfo.CharacterFind.StartHandle
		resultJson.TLV.TLVPayload.EndHandle = unifyInfo.CharacterFind.EndHandle
	case packets.TLVCharConfReqMsg:
		resultJson.TLV.TLVMsgType = packets.TLVCharConfReqMsg
		resultJson.TLV.TLVPayload.DevMac = unifyInfo.TerminalMac
		resultJson.TLV.TLVPayload.CCCDHandle = unifyInfo.ServiceNode.CCCDHanle
		resultJson.TLV.TLVPayload.CharHandle = unifyInfo.ServiceNode.CharacterHandle
		resultJson.TLV.TLVPayload.CCCDHandleValue = "01"
	case packets.TLVCharOptReqMsg:
		resultJson.TLV.TLVMsgType = packets.TLVCharOptReqMsg
		resultJson.TLV.TLVPayload.DevMac = unifyInfo.TerminalMac
		resultJson.TLV.TLVPayload.OpType = "02"
		resultJson.TLV.TLVPayload.ParaLength = globalutils.ConvertDecimalToHexStr(len(unifyInfo.CharacterOpteration.Parameter)>>1, globalconstants.BYTE_STR_FOUR)
		resultJson.TLV.TLVPayload.ReserveTwo = globalconstants.ReserveOneSmall
		resultJson.TLV.TLVPayload.ReserveThree = globalconstants.ReserveTwo
		resultJson.TLV.TLVPayload.CharHandle = unifyInfo.CharacterOpteration.CharacterHandle
		resultJson.TLV.TLVPayload.ParaValue = unifyInfo.CharacterOpteration.Parameter
	default:
		return resultJson, errors.New("unrecognized BLEMessage")
	}
	return
}

// 封装消息字节编码
func EnCodeForDownUdpMessage(jsonInfo packets.JsonUdpInfo) ([]byte, error) {
	var (
		enCodeResHeader, enCodeResBody, enCodeResAppMsg, enCodeResAppMsgBody, encodeResAppMsgBodyTLV strings.Builder
		encodeStr, tempStr, tempAppStr, tempAppTLVStr                                                string
		err                                                                                          error
	)
	if jsonInfo.PendCtrl&1 == 1 {
		enCodeResHeader.WriteString(jsonInfo.MessageHeader.Version)
		enCodeResHeader.WriteString(jsonInfo.MessageHeader.LinkMsgFrameSN)
		enCodeResHeader.WriteString(jsonInfo.MessageHeader.LinkMsgType)
		enCodeResHeader.WriteString(jsonInfo.MessageHeader.OpType)
	}
	if (jsonInfo.PendCtrl>>1)&1 == 1 {
		enCodeResBody.WriteString(jsonInfo.MessageBody.GwMac)
		enCodeResBody.WriteString(jsonInfo.MessageBody.ModuleID)
	}
	if (jsonInfo.PendCtrl>>2)&1 == 1 {
		enCodeResAppMsg.WriteString(jsonInfo.MessageAppHeader.SN)
		enCodeResAppMsg.WriteString(jsonInfo.MessageAppHeader.CtrlField)
		enCodeResAppMsg.WriteString(jsonInfo.MessageAppHeader.FragOffset)
		enCodeResAppMsg.WriteString(jsonInfo.MessageAppHeader.Type)
		enCodeResAppMsg.WriteString(jsonInfo.MessageAppHeader.OpType)
	}
	if (jsonInfo.PendCtrl>>3)&1 == 1 {
		enCodeResAppMsgBody.WriteString(jsonInfo.MessageAppBody.ErrorCode) //特征通告应答

		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVMsgType)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.ScanAble)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.ReserveOne)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.AddrType)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.DevMac)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.ServiceUUID)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.StartHandle)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.EndHandle)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.OpType)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.ReserveTwo)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.ParaLength)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.ScanType)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.ServiceHandle)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.CharHandle)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.CharacterUUID)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.CCCDHandle)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.CharHandleValue)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.CCCDHandleValue)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.ScanPhys)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.ReserveThree)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.ParaValue)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.ScanInterval)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.ScanWindow)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.ScanTimeout)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.ConnInterval)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.ConnLatency)
		encodeResAppMsgBodyTLV.WriteString(jsonInfo.MessageAppBody.TLV.TLVPayload.SupervisionWindow)
	}
	switch jsonInfo.PendCtrl {
	case globalconstants.CtrlLinkedMsgHeader:
		tempStr = enCodeResHeader.String()
		encodeStr, err = globalutils.InsertString(tempStr,
			globalutils.ConvertDecimalToHexStr((len(tempStr)+globalconstants.EncodeInsertLen)>>1, globalconstants.EncodeInsertLen),
			globalconstants.EncodeInsertMessageHeaderIndex, globalconstants.LOCAL_MESSAGE_MESSAGEHEAD)
	case globalconstants.CtrlLinkedMsgHeadWithBoy:
		tempStr = enCodeResHeader.String() + enCodeResBody.String()
		tempStr, err = globalutils.InsertString(tempStr,
			globalutils.ConvertDecimalToHexStr((len(tempStr)+globalconstants.EncodeInsertLen)>>1, globalconstants.EncodeInsertLen),
			globalconstants.EncodeInsertMessageHeaderIndex, globalconstants.LOCAL_MESSAGE_MESSAGEHEAD)
		encodeStr = tempStr
	case globalconstants.CtrlLinkedMsgWithMsgAppHeader:
		tempStr = enCodeResHeader.String() + enCodeResBody.String()
		tempAppStr = enCodeResAppMsg.String()
		tempAppStr, err = globalutils.InsertString(tempAppStr,
			globalutils.ConvertDecimalToHexStr((len(tempStr)+globalconstants.EncodeInsertLen)>>1, globalconstants.EncodeInsertLen),
			globalconstants.EncodeInsertMessageAppHeaderIndex, globalconstants.LOCAL_MESSAGE_MESSAGEAPPHEAD)
		if err != nil {
			return nil, err
		}
		tempStr, err = globalutils.InsertString(tempStr,
			globalutils.ConvertDecimalToHexStr((len(tempAppStr)+len(tempStr)+globalconstants.EncodeInsertLen)>>1, globalconstants.EncodeInsertLen),
			globalconstants.EncodeInsertMessageHeaderIndex, globalconstants.LOCAL_MESSAGE_MESSAGEHEAD)
		encodeStr = tempStr + tempAppStr
	default:
		tempStr = enCodeResHeader.String() + enCodeResBody.String()
		tempAppStr = enCodeResAppMsg.String() + enCodeResAppMsgBody.String()
		tempAppTLVStr = encodeResAppMsgBodyTLV.String()
		if len(tempAppTLVStr) > 0 {
			tempAppTLVStr, err = globalutils.InsertString(tempAppTLVStr,
				globalutils.ConvertDecimalToHexStr((len(tempAppTLVStr)+globalconstants.EncodeInsertLen)>>1, globalconstants.EncodeInsertLen),
				globalconstants.EncodeInsertMessageTLVIndex, globalconstants.LOCAL_MESSAGE_MESSAGEAPPBODY)
			if err != nil {
				return nil, err
			}
		}
		tempAppStr, err = globalutils.InsertString(tempAppStr, globalutils.ConvertDecimalToHexStr((len(tempAppTLVStr)+len(tempAppStr)+globalconstants.EncodeInsertLen)>>1, globalconstants.EncodeInsertLen),
			globalconstants.EncodeInsertMessageAppHeaderIndex, globalconstants.LOCAL_MESSAGE_MESSAGEAPPHEAD)
		if err != nil {
			return nil, err
		}
		tempStr, err = globalutils.InsertString(tempStr,
			globalutils.ConvertDecimalToHexStr((len(tempStr)+len(tempAppStr)+len(tempAppTLVStr)+globalconstants.EncodeInsertLen)>>1, globalconstants.EncodeInsertLen),
			globalconstants.EncodeInsertMessageHeaderIndex, globalconstants.LOCAL_MESSAGE_MESSAGEHEAD)
		encodeStr = tempStr + tempAppStr + tempAppTLVStr
	}
	if err != nil {
		return nil, err
	}
	enCodeBytes, err1 := hex.DecodeString(encodeStr)
	if err1 != nil {
		return nil, err1
	}
	return enCodeBytes, nil
}
